package it.uniroma3.dia;

import it.uniroma3.dia.models.ErrorRatingMovie;
import it.uniroma3.dia.models.RatingMovie;
import it.uniroma3.dia.predictors.AbstractPredictor;
import it.uniroma3.dia.predictors.ItemBasedPredictor;
import it.uniroma3.dia.predictors.ItemBasedPredictor.ItemBasedTuningParameter;
import it.uniroma3.dia.reader.DBRatingMovieReader;
import it.uniroma3.dia.reader.IRatingMovieRW;
import it.uniroma3.dia.training.ErrorWatcher;
import it.uniroma3.dia.utils.Utils;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

	public static void main(String[] args) {
		try{
			int Kfold = 3;
			int numberOfTraining = 20;
			int numberOfTestRecord = 20000;
			IRatingMovieRW ratingMovieReader = new DBRatingMovieReader(numberOfTestRecord);
			List<RatingMovie> ratingMovies = ratingMovieReader.readRatingMovies();
			System.out.println(ratingMovies.size()+" caricati, processamento in corso...");
			
			/*BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt") );
			for(RatingMovie ratingMovie : ratingMovies){
				String line = ratingMovie.getUserID()+"\t"+ratingMovie.getMovieID()+"\t"+ratingMovie.getTimestamp();
				line += "\n";
				writer.write(line);
			}
			
			writer.close();*/
			
			//[wpearsonItem=10.0, wactorRating=3.0, wgenreRating=5.0, wtagRating=9.0, wdirectorRating=1.0, wpearsonUser=7.0]
			int K = 5;
			ItemBasedTuningParameter randomTuning = ItemBasedPredictor.newItemBasedTuningParameter(10,3,5,10,2,7,8, K);
			launchPredictor(ratingMovies, randomTuning, 4, true);

		} catch(Exception ex){
			ex.printStackTrace();
		}
	}

	private static ErrorRatingMovie launchPredictor(List<RatingMovie> ratingMovies, ItemBasedTuningParameter tuningParameter, int numberOfThread, boolean watcher)
			throws InterruptedException {

		long startTime = System.currentTimeMillis();
		ExecutorService executor = Executors.newFixedThreadPool(numberOfThread);
		CountDownLatch countDownLatch = new CountDownLatch(numberOfThread);

		ConcurrentLinkedQueue<RatingMovie> concurredLinkedQueue = new ConcurrentLinkedQueue<RatingMovie>();
		concurredLinkedQueue.addAll(ratingMovies);
		List<AbstractPredictor> predictors = new ArrayList<AbstractPredictor>();
		

		for(int j = 0; j<numberOfThread; j++){	
			AbstractPredictor itemBasedPrediction = new ItemBasedPredictor(tuningParameter);
			itemBasedPrediction.setCountDownLatch(countDownLatch);
			itemBasedPrediction.setConcurredLinkedQueue(concurredLinkedQueue);
			executor.execute(itemBasedPrediction);
			predictors.add(itemBasedPrediction);
		}

		Timer timer = new Timer();
		if(watcher){
			TimerTask task = new ErrorWatcher(predictors);
			timer.schedule( task, 1000, 1000 );
		}
		countDownLatch.await();
		executor.shutdown();

		timer.cancel();

		long endTime = System.currentTimeMillis();
		long diff = endTime - startTime;

		ErrorRatingMovie errorRatingMovie = Utils.calculateErrorMeasure(predictors);
		double totalMAE = errorRatingMovie.getTotalMAE();
		double totalRMSE = errorRatingMovie.getTotalRMSE();
		int errorGreaterThan = errorRatingMovie.getErrorGreaterThan();
		int total = errorRatingMovie.getTotal();

		System.out.println("tuning = "+tuningParameter.toString());
		System.out.print(total+" record elaborati in "+diff+" ms ("+(double)total/(diff/1000)+" movie\\s). MAE = "+(totalMAE/total)+", RMSE = "+Math.sqrt(totalRMSE/total) +", ERROR>1.5 = "+errorGreaterThan);

		return errorRatingMovie;
	}
}